package com.suifeng.mq.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.suifeng.mq.common.constants.MqMessageConstant;
import com.suifeng.mq.common.enums.OrderStatusEnum;
import com.suifeng.mq.common.exception.NoStockException;
import com.suifeng.mq.common.to.OrderTo;
import com.suifeng.mq.common.utils.R;
import com.suifeng.mq.common.utils.UuidUtils;
import com.suifeng.mq.common.vo.OrderItemVo;
import com.suifeng.mq.common.vo.StockGoodsLockVo;
import com.suifeng.mq.order.dao.MqMessageDao;
import com.suifeng.mq.order.dao.OrderDao;
import com.suifeng.mq.order.dto.OrderCreateDTO;
import com.suifeng.mq.order.entity.MqMessage;
import com.suifeng.mq.order.entity.OrderEntity;
import com.suifeng.mq.order.entity.OrderItemEntity;
import com.suifeng.mq.order.feign.StockFeignService;
import com.suifeng.mq.order.service.OrderItemService;
import com.suifeng.mq.order.service.OrderService;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;


@Service
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    @Autowired
    private OrderItemService orderItemService;

    @Autowired
    private StockFeignService stockFeignService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private MqMessageDao mqMessageDao;

    @Override
    @Transactional
//    @GlobalTransactional
    public void createOrder() {
        OrderCreateDTO orderCreateDTO = generateOrder();
        saveOrder(orderCreateDTO);

        StockGoodsLockVo lockVo = new StockGoodsLockVo();
        lockVo.setOrderSn(orderCreateDTO.getOrder().getOrderSn());
        List<OrderItemVo> lockItemVos = orderCreateDTO.getOrderItems().stream().map(item -> {
            OrderItemVo itemVo = new OrderItemVo();
            BeanUtils.copyProperties(item, itemVo);
            return itemVo;

        }).collect(Collectors.toList());
        lockVo.setLocks(lockItemVos);

        //远程锁库存
        //库存成功了，但是网络原因超时了，订单回滚，库存不回滚
        R r = stockFeignService.orderLockStock(lockVo);

        if (r.getCode() == 0) {
            //库存锁成功了

//           int i = 10 / 0;//订单回滚，库存不会滚
            //TODO 订单创建成功发送消息给MQ
            OrderEntity order = orderCreateDTO.getOrder();
            rabbitTemplate.convertAndSend("order-event-exchange", "order.create.order", order);
            //将要发送的消息保存到数据库一份
//            saveMqMessage(order);

            return;
        } else {
            //锁定失败
            String msg = (String) r.get("msg");
            throw new NoStockException(msg);
        }

    }

    private void saveMqMessage(OrderEntity order) {
        MqMessage mqMessage = new MqMessage();
        mqMessage.setMessageId(order.getOrderSn());
        mqMessage.setContent(JSON.toJSONString(order));
        mqMessage.setClassType(MqMessage.class.toString());
        mqMessage.setToExchange("order-event-exchange");
        mqMessage.setRoutingKey("order.create.order");
        mqMessage.setMessageStatus(MqMessageConstant.MQ_MSG_CREATE);
        mqMessage.setCreateTime(new Date());
        mqMessage.setUpdateTime(new Date());
        mqMessageDao.insert(mqMessage);
    }

    /**
     * 初始化订单数据
     *
     * @return
     */
    public OrderCreateDTO generateOrder() {
        OrderCreateDTO orderCreateDTO = new OrderCreateDTO();

        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(UuidUtils.generateUuid());
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setCreateTime(new Date());

        List<OrderItemEntity> itemList = new ArrayList<>();


        for (long i = 1; i <= 2; i++) {
            OrderItemEntity orderItemEntity = new OrderItemEntity();
            orderItemEntity.setGoodsId(i);
            orderItemEntity.setGoodsName("小米" + i);
            orderItemEntity.setGoodsNum(1);
            orderItemEntity.setOrderSn(orderEntity.getOrderSn());
            orderItemEntity.setCreateTime(orderEntity.getCreateTime());
            itemList.add(orderItemEntity);
        }

        orderCreateDTO.setOrder(orderEntity);
        orderCreateDTO.setOrderItems(itemList);
        return orderCreateDTO;
    }


    /**
     * 保存订单的所有数据
     *
     * @param orderCreateDTO
     */
    private void saveOrder(OrderCreateDTO orderCreateDTO) {
        OrderEntity orderEntity = orderCreateDTO.getOrder();
        orderEntity.setCreateTime(new Date());
        this.save(orderEntity);

        List<OrderItemEntity> orderItems = orderCreateDTO.getOrderItems();
        orderItemService.saveBatch(orderItems);
    }

    @Override
    public void closeOrder(OrderEntity orderEntity) {

        //查询当前订单的最新状态
        OrderEntity entity = this.getById(orderEntity.getId());
        if (entity.getStatus() == OrderStatusEnum.CREATE_NEW.getCode()) {
            //关单
            OrderEntity update = new OrderEntity();
            update.setId(orderEntity.getId());
            update.setStatus(OrderStatusEnum.CANCLED.getCode());
            this.updateById(update);
            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(entity, orderTo);
            //关单后给MQ发消息
            try {
                //TODO 保证消息一定会发送出去，每一个消息都可以做好日志记录（给数据库保存每一个消息的详细信息）
                //TODO 定期扫描数据库将失败的消息再发送一遍
                //order-release-other  模拟路由键错误 消息发送会失败
                rabbitTemplate.convertAndSend("order-event-exchange", "order.release.other", orderTo);
                //将消息保存到数据库
//                saveMqMessage(entity);
            } catch (Exception e) {
                //TODO 将没发送成功的消息进行重试发送（可以写一个定时任务定时执行）
            }
        }
    }

    @Override
    public OrderEntity getOrderStatus(String orderSn) {
        return this.getOne(new LambdaQueryWrapper<OrderEntity>().eq(OrderEntity::getOrderSn, orderSn));
    }


}
